package framework;

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.Timer;

/**
 * This class implements DisplayInterface and ActionListener. This class is also
 * a listener for the timer. Each time the timer triggers an event, this class
 * catches a signal and calls method actionPerformed.
 * 
 */
public class DisplayImpl implements DisplayInterface, ActionListener {
	// Initial time delay
	private final static int DELAY_INI = 200;
	// Stores the main frame
	private static JFrame frame;
	// Stores array of selfdraw cells
	private SelfDrawCellInterface[] cs;
	// Stores the automaton plugin
	private AutomatonPluginInterface ap;
	// Stores the timer
	private Timer timer;
	// Stores components to be drawn on frame
	private DisplayUtilStage stage;
	// the parameters for the layout
	private final static int margin = 1;
	private final static int frameScale = 2;

	/**
	 * Constructor that initializes fields and customizse the new frame
	 * 
	 * @param SelfDrawCellInterface[] cs - all the selfDrawCells
	 * @param AutomatonPluginInterface ap - the automaton plugin
	 * @param VisualizerPluginInterface vp - the visualizer plugin
	 */
	public DisplayImpl(SelfDrawCellInterface[] cs, AutomatonPluginInterface ap,
			VisualizerPluginInterface vp) {
		// Initialize selfdraw cell array
		this.cs = cs;
		// Initialize automaton plugin
		this.ap = ap;

		// Create new frame
		frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Compute suitable size of board display
		int xsize = (ap.getBoard().getTotalColNum())
				* (vp.getPreferredDimension().width);
		int ysize = (ap.getBoard().getTotalRowNum())
				* (vp.getPreferredDimension().height);

		// Customize frame
		frame.setVisible(true);
		frame.getContentPane().setLayout(
				new BoxLayout(frame.getContentPane(), BoxLayout.PAGE_AXIS));
		frame.setResizable(false);

		// Create new layout for stage
		GridLayout layout = new GridLayout(ap.getBoard().getTotalColNum(), ap
				.getBoard().getTotalRowNum(), margin, margin);

		// Initialize timer
		timer = new Timer(DELAY_INI, this);
		ap.getBoard().initBoard();

		// Initialize stage
		stage = new DisplayUtilStage(frame, frameScale * xsize, frameScale
				* ysize, layout, cs);
		// Initialize mouse
		DisplayUtilMouse mouse = new DisplayUtilMouse(ap, stage);

		// Add listeners to stage
		stage.getPane().addMouseListener(mouse);
		stage.getPane().addMouseMotionListener(mouse);

		// Initialize display controller
		new DisplayUtilController(frame, timer, vp, ap, stage, mouse);

		// Pack frame to fit the stage and controller
		frame.pack();
	}

	/**
	 * Updates states of all selfdraw cells
	 * 
	 * @param SelfDrawCell[] scells - all the selfDrawCells that need to be updated
	 */
	private void updateSelfDrawCell(SelfDrawCellInterface[] scells) {
		// Create new list to store all neighbors of each cell
		List<int[]> NbrLists = new ArrayList<int[]>();
		// For each cell
		for (int i = 0; i < scells.length; i++) {
			// Get neighbors of cell
			CellInterface[] Nbr = ap.getBoard().getNeighbors(
					scells[i].getCell());
			// Create array to store neighbor states
			int[] NbrStates = new int[ap.getBoard().getNbrNum()];
			for (int n = 0; n < Nbr.length; n++) {
				if (Nbr[n] != null) {
					NbrStates[n] = Nbr[n].getState();
				}
			}
			NbrLists.add(NbrStates);
		}
		// Update cell states
		for (int n = 0; n < scells.length; n++) {
			scells[n].updateCell(NbrLists.get(n));
		}
	}

	/**
	 * Listener method that calls drawBoard() each time Timer triggers an event.
	 * drawBoard() draws board with updated cell states.
	 */
	public void actionPerformed(ActionEvent e) {
		drawBoard();
	}

	/**
	 * Updates all selfdraw cells and then draws all cells using the
	 * stage.redraw() method.
	 */
	public void drawBoard() {
		// Update cell states
		updateSelfDrawCell(cs);
		// Redraw
		stage.redraw();
	}
}
